/**
 * File: ArcImpl.java
 * Created by: mhaimel
 * Created on: Mar 5, 2010
 * CVS:  $Id: ArcImpl.java 1.0 Mar 5, 2010 1:27:11 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.model.graph.impl;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.StringUtils;

import uk.ac.ebi.curtain.model.graph.Arc;
import uk.ac.ebi.curtain.model.graph.Node;
import uk.ac.ebi.curtain.model.graph.NodeEdge;

/**
 * @author mhaimel
 *
 */
public class ArcImpl<N,A> implements Arc<N, A> {
	private final Long id;
	private final Node<N, A> left;
	private final boolean leftNodePos;
	private final Node<N, A> right;
	private final boolean rightNodePos;
	private final AtomicReference<A> value;
	private final AtomicInteger status;
	
	public ArcImpl( Long id,Arc<N,A> arc){
		this(id,arc.getLeft(),arc.getRight(),arc.onLeftNode(), arc.onRightNode());
		this.value.set(arc.getValue());
	}
	
	public ArcImpl(Long id, Node<N,A> left, Node<N,A> right, NodeEdge leftNodePos, NodeEdge rightNodePos) {
		super();
		this.id = id;
		this.left = left;
		this.leftNodePos = leftNodePos.asBoolean();
		this.right = right;
		this.rightNodePos = rightNodePos.asBoolean();
		this.value = new AtomicReference<A>();
		this.status = new AtomicInteger(0);
	}
	
	@Override
	public String toString() {
		return StringUtils.join(new Object[]{
			id,
			"left="+left.getId(),
			"right="+right.getId(),
			"leftPos="+leftNodePos,
			"rightPos="+rightNodePos,
			"hasValue="+(value)
		},';');
	}
	
	public Long getId() {
		return id;
	}
	
	public void setValue(A value) {
		this.value.set(value);
	}
	
	/**
	 * @return the left
	 */
	public Node<N, A> getLeft() {
		return left;
	}
	
	public NodeEdge onLeftNode(){
		return NodeEdge.fromBoolean(leftNodePos);
	}
	
	public NodeEdge onRightNode(){
		return NodeEdge.fromBoolean(rightNodePos);
	}
	
	public AtomicInteger getStatus() {
		return status;
	}
	
	public void setStatus(Integer status) {
		this.status.set(status);
	}

	/**
	 * @return the right
	 */
	public Node<N, A> getRight() {
		return right;
	}

	public A getValue(){
		return value.get();
	}
	
	public AtomicReference<A> getAtomicValue(){
		return this.value;
	}
	
	public NodeEdge onOtherNode(Node<N, A> node){
		if(getRight().equals(node)){
			return onLeftNode();
		} else if(getLeft().equals(node)){
			return onRightNode();
		} else {
			throw new IllegalArgumentException("ArcImpl does not have connect to " + node + "! " + this);
		}
	}
	
	public NodeEdge onNode(Node<N, A> node){
		if(getRight().equals(node)){
			return onRightNode();
		} else if(getLeft().equals(node)){
			return onLeftNode();
		} else {
			throw new IllegalArgumentException("ArcImpl does not have connect to " + node + "! " + this);
		}
	}
	
	public Node<N, A> getOtherNode(Node<N, A> node){
		if(getRight().equals(node)){
			return getLeft();
		} else if(getLeft().equals(node)){
			return getRight();
		} else {
			throw new IllegalArgumentException("ArcImpl does not have connect to " + node + "! " + this);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((left == null) ? 0 : left.getId().hashCode());
		result = prime * result + ((right == null) ? 0 : right.getId().hashCode());
		result = prime * result + ((value.get() == null) ? 0 : value.get().hashCode());
		result = prime * result + Boolean.valueOf(leftNodePos).hashCode();
		result = prime * result + Boolean.valueOf(rightNodePos).hashCode();
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ArcImpl other = (ArcImpl) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (left == null) {
			if (other.left != null)
				return false;
		} else if (!left.getId().equals(other.left.getId()))
			return false;
		if (right == null) {
			if (other.right != null)
				return false;
		} else if (!right.getId().equals(other.right.getId()))
			return false;
		if (value.get() == null) {
			if (other.value.get() != null)
				return false;
		} else if (!value.get().equals(other.value.get()))
			return false;
		if(rightNodePos != other.rightNodePos)
			return false;
		if(leftNodePos != other.leftNodePos)
			return false;
		return true;
	}

	@Override
	public void detachFromNodes() {
		left.removeArc(this);
		right.removeArc(this);
	}
	
	
}
